home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / MTRECAL.PAK / RECALDOC.CPP < prev    next >
C/C++ Source or Header  |  1997-05-06  |  9KB  |  316 lines

  1. // recaldoc.cpp : implementation of the CRecalcDoc class
  2. //
  3. // This is a part of the Microsoft Foundation Classes C++ library.
  4. // Copyright (C) 1992-1995 Microsoft Corporation
  5. // All rights reserved.
  6. //
  7. // This source code is only intended as a supplement to the
  8. // Microsoft Foundation Classes Reference and related
  9. // electronic documentation provided with the library.
  10. // See these sources for detailed information regarding the
  11. // Microsoft Foundation Classes product.
  12.  
  13. #include "stdafx.h"
  14. #include "mtrecalc.h"
  15. #include "calcthrd.h"
  16. #include "recaldoc.h"
  17. #include "slowcalc.h"
  18. #include "speeddlg.h"
  19. #include "recalcvw.h"
  20.  
  21. #ifdef _DEBUG
  22. #undef THIS_FILE
  23. static char BASED_CODE THIS_FILE[] = __FILE__;
  24. #endif
  25.  
  26. /////////////////////////////////////////////////////////////////////////////
  27. // CRecalcDoc
  28.  
  29. IMPLEMENT_DYNCREATE(CRecalcDoc, CDocument)
  30.  
  31. BEGIN_MESSAGE_MAP(CRecalcDoc, CDocument)
  32.     ON_COMMAND_RANGE(ID_SINGLE_THREAD, ID_UI_THREAD, OnDemoThread)
  33.     ON_UPDATE_COMMAND_UI_RANGE(ID_SINGLE_THREAD, ID_UI_THREAD, OnUpdateDemoThread)
  34.     //{{AFX_MSG_MAP(CRecalcDoc)
  35.     ON_COMMAND(ID_RECALC_SPEED, OnRecalcSpeed)
  36.     ON_COMMAND(ID_KILL_WORKER_THREAD, OnKillWorkerThread)
  37.     ON_UPDATE_COMMAND_UI(ID_KILL_WORKER_THREAD, OnUpdateKillWorkerThread)
  38.     //}}AFX_MSG_MAP
  39. END_MESSAGE_MAP()
  40.  
  41. /////////////////////////////////////////////////////////////////////////////
  42. // CRecalcDoc construction/destruction
  43.  
  44. CRecalcDoc::CRecalcDoc()
  45. {
  46.     m_nInt1 = m_nInt2 = m_nSum = 0;
  47.     m_bRecalcNeeded = m_bRecalcInProgress = FALSE;
  48.     m_nRecalcSpeedSeconds = 5;
  49.  
  50.     m_nCurrentDemoCommand = ID_SINGLE_THREAD;
  51.  
  52.     m_hEventStartRecalc = CreateEvent(NULL, FALSE, FALSE, NULL); // auto reset, initially reset
  53.     m_hEventRecalcDone = CreateEvent(NULL, TRUE, TRUE, NULL); // manual reset, initially set
  54.     m_hEventKillRecalcThread = CreateEvent(NULL, FALSE, FALSE, NULL); // auto reset, initially reset
  55.     m_hEventRecalcThreadKilled = CreateEvent(NULL, FALSE, FALSE, NULL); // auto reset, initially reset
  56.  
  57.     m_recalcThreadInfo.m_hEventStartRecalc = m_hEventStartRecalc;
  58.     m_recalcThreadInfo.m_hEventRecalcDone = m_hEventRecalcDone;
  59.     m_recalcThreadInfo.m_hEventKillRecalcThread = m_hEventKillRecalcThread;
  60.     m_recalcThreadInfo.m_hEventRecalcThreadKilled = m_hEventRecalcThreadKilled;
  61.  
  62.     m_pRecalcWorkerThread = NULL;
  63. }
  64.  
  65. CRecalcDoc::~CRecalcDoc()
  66. {
  67.     // In this application, the document owns the worker thread.
  68.     // The document's destructor is responsible for killing the active worker
  69.     // thread.
  70.  
  71.     // It's a good idea to wait for the worker thread to notify via a
  72.     // "thread killed" event that it has killed itself. Otherwise, in the case
  73.     // where the app is terminating, is possible (even if unlikely) that it
  74.     // will detect a memory leak of the CWinThread object before the
  75.     // CWinThread object has had a chance to auto-delete itself.
  76.  
  77.     DWORD dwExitCode;
  78.     if (m_pRecalcWorkerThread != NULL &&
  79.         GetExitCodeThread(m_pRecalcWorkerThread->m_hThread, &dwExitCode) &&
  80.         dwExitCode == STILL_ACTIVE)
  81.     {
  82.         // Kill the worker thread by setting the "kill thread" event.
  83.         // See comment in OnKillWorkerThread for explanation of the sequence
  84.         // of the "kill thread" and "start recalc" events.
  85.         SetEvent(m_hEventKillRecalcThread);
  86.         SetEvent(m_hEventStartRecalc);
  87.         WaitForSingleObject(m_hEventRecalcThreadKilled, INFINITE);
  88.     }
  89. }
  90.  
  91. /////////////////////////////////////////////////////////////////////////////
  92. // CRecalcDoc overrides
  93.  
  94.  
  95. BOOL CRecalcDoc::OnNewDocument()
  96. {
  97.     if (!CDocument::OnNewDocument())
  98.         return FALSE;
  99.  
  100.     return TRUE;
  101. }
  102.  
  103. BOOL CRecalcDoc::OnSaveDocument(LPCTSTR lpszPathName)
  104. {
  105.     if (m_bRecalcInProgress)
  106.     {
  107.         if (AfxMessageBox(IDS_CONTINUE_RECALC_BEFORE_SAVE, MB_YESNO) == IDYES)
  108.         {
  109.             BeginWaitCursor();
  110.             WaitForSingleObject(m_hEventRecalcDone, INFINITE);
  111.             RecalcDone();
  112.             EndWaitCursor();
  113.         }
  114.         else
  115.         {
  116.             AfxMessageBox(IDS_FILE_NOT_SAVED);
  117.             return FALSE;
  118.         }
  119.     }
  120.     else if (m_bRecalcNeeded) // recalc needed but not in progress
  121.     {
  122.         if (AfxMessageBox(IDS_NEED_RECALC_BEFORE_SAVE, MB_YESNO) == IDYES)
  123.         {
  124.             BeginWaitCursor();
  125.  
  126.             // Update the document from data in the view and start recalculation.
  127.             POSITION pos = GetFirstViewPosition();
  128.             ASSERT(pos != NULL);
  129.             CView* pView = GetNextView(pos);
  130.             pView->SendMessage(WM_USER_RECALC_NOW, 0, 0);
  131.  
  132.             WaitForSingleObject(m_hEventRecalcDone, INFINITE);
  133.             RecalcDone();
  134.             EndWaitCursor();
  135.         }
  136.         else
  137.         {
  138.             AfxMessageBox(IDS_FILE_NOT_SAVED);
  139.             return FALSE;
  140.         }
  141.     }
  142.     return CDocument::OnSaveDocument(lpszPathName);
  143. }
  144.  
  145.  
  146. /////////////////////////////////////////////////////////////////////////////
  147. // CRecalcDoc serialization
  148.  
  149. void CRecalcDoc::Serialize(CArchive& ar)
  150. {
  151.     if (ar.IsStoring())
  152.     {
  153.         ar << (WORD)m_nInt1;
  154.         ar << (WORD)m_nInt2;
  155.         ar << (WORD)m_nSum;
  156.     }
  157.     else
  158.     {
  159.         WORD w;
  160.         ar >> w;
  161.         m_nInt1 = w;
  162.         ar >> w;
  163.         m_nInt2 = w;
  164.         ar >> w;
  165.         m_nSum = w;
  166.     }
  167. }
  168.  
  169. /////////////////////////////////////////////////////////////////////////////
  170. // CRecalcDoc diagnostics
  171.  
  172. #ifdef _DEBUG
  173. void CRecalcDoc::AssertValid() const
  174. {
  175.     CDocument::AssertValid();
  176. }
  177.  
  178. void CRecalcDoc::Dump(CDumpContext& dc) const
  179. {
  180.     CDocument::Dump(dc);
  181. }
  182. #endif //_DEBUG
  183.  
  184.  
  185. /////////////////////////////////////////////////////////////////////////////
  186. // CRecalcDoc operations and implementation
  187.  
  188. void CRecalcDoc::UpdateInt1AndInt2(int n1, int n2, BOOL bForceRecalc)
  189. {
  190.     if (!bForceRecalc && m_nInt1 == n1 && m_nInt2 == n2)
  191.         return;  // recalculation not needed
  192.  
  193.     SetModifiedFlag();
  194.     m_nInt1 = n1;
  195.     m_nInt2 = n2;
  196.  
  197.     m_bRecalcNeeded = m_bRecalcInProgress = TRUE;
  198.     // Synchronize the int1 and int2 fields in the other views,
  199.     // and show "recalculating..." in the sum field.
  200.     UpdateAllViews(NULL);
  201.  
  202.     switch (m_nCurrentDemoCommand)
  203.     {
  204.         case ID_SINGLE_THREAD:
  205.             RecalcInSingleThread();
  206.             break;
  207.  
  208.         case ID_WORKER_THREAD:
  209.             RecalcInSecondThread();
  210.             break;
  211.         default:
  212.             ASSERT(FALSE);
  213.     }
  214. }
  215.  
  216.  
  217. void CRecalcDoc::RecalcInSingleThread()
  218. {
  219.     SlowAdd(m_nInt1, m_nInt2, m_nSum, NULL, m_nRecalcSpeedSeconds, AfxGetMainWnd()->m_hWnd);
  220.  
  221.     m_bRecalcNeeded = m_bRecalcInProgress = FALSE;
  222.  
  223.     // Update the "sum" field.
  224.     UpdateAllViews(NULL, UPDATE_HINT_SUM);
  225.  
  226. }
  227.  
  228. void CRecalcDoc::RecalcInSecondThread()
  229. {
  230.     if (m_pRecalcWorkerThread == NULL)
  231.     {
  232.         // Begin the worker thread.  It is ok to fill in the CThreadInfo
  233.         // structure after the thread has started, because the thread
  234.         // waits for the "start recalc" event before referring to the structure.
  235.         m_pRecalcWorkerThread =
  236.             AfxBeginThread(RecalcThreadProc, &m_recalcThreadInfo);
  237.     }
  238.  
  239.     m_recalcThreadInfo.m_nInt1 = m_nInt1;
  240.     m_recalcThreadInfo.m_nInt2 = m_nInt2;
  241.     POSITION pos = GetFirstViewPosition();
  242.     ASSERT(pos != NULL);
  243.     CView* pView = GetNextView(pos);
  244.     ASSERT(pView != NULL);
  245.     m_recalcThreadInfo.m_hwndNotifyRecalcDone = pView->m_hWnd;
  246.     m_recalcThreadInfo.m_hwndNotifyProgress = AfxGetMainWnd()->m_hWnd;
  247.     m_recalcThreadInfo.m_nRecalcSpeedSeconds = m_nRecalcSpeedSeconds;
  248.  
  249.     // The events are initially set or reset in the CreateEvent call;
  250.     // but they may be left in an improperly initialized state if
  251.     // a worker thread has been previously started and then prematurely
  252.     // killed.  Set/reset the events to the proper initial state.
  253.     // Set the "start recalc" event last, since it is the event the
  254.     // triggers the starting of the worker thread recalculation.
  255.     SetEvent(m_hEventRecalcDone);
  256.     ResetEvent(m_hEventKillRecalcThread);
  257.     ResetEvent(m_hEventRecalcThreadKilled);
  258.     SetEvent(m_hEventStartRecalc);
  259.     // RecalcDone() will be called by the view when the thread sends a
  260.     // WM_USER_RECALC_DONE message.
  261. }
  262.  
  263. void CRecalcDoc::RecalcDone()
  264. {   // Called by the view when the worker thread sends a WM_USER_RECALC_DONE message.
  265.  
  266.     m_nSum = m_recalcThreadInfo.m_nSum;
  267.     m_bRecalcNeeded = m_bRecalcInProgress = FALSE;
  268.     UpdateAllViews(NULL, UPDATE_HINT_SUM);
  269. }
  270.  
  271.  
  272. /////////////////////////////////////////////////////////////////////////////
  273. // CRecalcDoc commands
  274.  
  275. void CRecalcDoc::OnDemoThread(UINT nCmdID)
  276. {
  277.     m_nCurrentDemoCommand = nCmdID;
  278. }
  279.  
  280. void CRecalcDoc::OnUpdateDemoThread(CCmdUI* pCmdUI)
  281. {
  282.     pCmdUI->SetCheck(pCmdUI->m_nID == m_nCurrentDemoCommand);
  283. }
  284.  
  285. void CRecalcDoc::OnRecalcSpeed()
  286. {
  287.     CSpeedDlg dlg;
  288.     dlg.m_nRecalcSpeedSeconds = m_nRecalcSpeedSeconds;
  289.     if (dlg.DoModal() == IDOK)
  290.         m_nRecalcSpeedSeconds = dlg.m_nRecalcSpeedSeconds;
  291. }
  292.  
  293. void CRecalcDoc::OnKillWorkerThread()
  294. {
  295.     // The worker thread periodically checks for the "kill recalc" event
  296.     // during its execution of a recalculation.  When the worker thread
  297.     // is not currently recalculating, it is waiting for a "start recalc"
  298.     // event.  Therefore, in order to kill the thread that might be waiting
  299.     // for the "start recalc" event, it is necessary to first set the
  300.     // "start recalc" event.
  301.     SetEvent(m_hEventKillRecalcThread);
  302.     SetEvent(m_hEventStartRecalc);
  303.  
  304.     WaitForSingleObject(m_hEventRecalcThreadKilled, INFINITE);
  305.  
  306.     m_pRecalcWorkerThread = NULL;
  307.     m_bRecalcInProgress = FALSE; // but m_bRecalcNeeded is still TRUE
  308.     UpdateAllViews(NULL, UPDATE_HINT_SUM);
  309. }
  310.  
  311. void CRecalcDoc::OnUpdateKillWorkerThread(CCmdUI* pCmdUI)
  312. {
  313.     pCmdUI->Enable(m_nCurrentDemoCommand == ID_WORKER_THREAD
  314.             && m_pRecalcWorkerThread != NULL);
  315. }
  316.